package com.ryan.mdp.annotation.parser;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.ryan.mdp.annotation.MdpMethod;
import com.ryan.mdp.annotation.MessageProperties;
import com.ryan.mdp.annotation.MessageProperty;
import com.ryan.mdp.callback.MdpCallback;

/**
 * MdpMethod Annotation解析器
 * 
 */
public class MdpMethodParser {
    private static Map<Class<?>, MdpServiceConfig> methodMap = new ConcurrentHashMap<Class<?>, MdpServiceConfig>();

    /**
     * 解析MDP interface接口class
     * 
     * @param mdpInterface
     * @return
     */
    public static synchronized MdpServiceConfig parseMdpInterface(Class<?> mdpInterface) {
        MdpServiceConfig mdpServiceConfig = methodMap.get(mdpInterface);
        if (mdpServiceConfig != null) {
            return mdpServiceConfig;
        } else {
            mdpServiceConfig = new MdpServiceConfig();
            Method[] methods = mdpInterface.getMethods();
            for (int i = 0; i < methods.length; i++) {
                MdpMethodConfig methodConfig = new MdpMethodConfig(methods[i]);
                Class<?> returnType = methods[i].getReturnType();
                boolean sync = false;
                boolean callback = false;
                if (returnType != Void.TYPE) {
                    sync = true;
                }
                methodConfig.setSync(sync);
                Class<?>[] parameterTypes = methods[i].getParameterTypes();
                if (parameterTypes != null) {
                    for (int j = 0; j < parameterTypes.length; j++) {
                        if (parameterTypes[j].isAssignableFrom(MdpCallback.class)) {
                            callback = true;
                            break;
                        }
                    }
                    methodConfig.setCallback(callback);
                    if (sync && callback) {
                        throw new IllegalArgumentException("Wrong method signature: " + methods[i] + "of "
                                + mdpInterface.getName() + ", the mdp method can't have returning value "
                                + "and MdpCallback type parameter at same time.");
                    }
                    if (methods[i].isAnnotationPresent(MdpMethod.class)) {
                        MdpMethod mdpMethodAnnotation = methods[i].getAnnotation(MdpMethod.class);
                        methodConfig.setTimeout(mdpMethodAnnotation.timeout());
                        methodConfig.setTtl(mdpMethodAnnotation.ttl());
                        methodConfig.setPriority(mdpMethodAnnotation.priority());
                        methodConfig.setCache(mdpMethodAnnotation.cache());
                        methodConfig.setCacheTtl(mdpMethodAnnotation.cacheTtl());
                    }
                    parseMethodMsgPropertyAnnotations(methods[i], methodConfig);
                }
                mdpServiceConfig.put(methods[i], methodConfig);
            }
            methodMap.put(mdpInterface, mdpServiceConfig);
            return mdpServiceConfig;
        }
    }

    public static MdpServiceConfig getMdpServiceConfig(Class<?> mdpInterface) {
        MdpServiceConfig mdpServiceConfig = methodMap.get(mdpInterface);
        return mdpServiceConfig;
    }

    /**
     * 解析接口方法上的MessageProperty Annotation
     * 
     * @param method
     * @param methodConfig
     */
    private static void parseMethodMsgPropertyAnnotations(Method method, MdpMethodConfig methodConfig) {
        // 解析参数上的MessageProperty Annotation
        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        List<List<MessageProperty>> paramMsgProperties = new ArrayList<List<MessageProperty>>();
        for (int paramIndex = 0; paramIndex < paramAnnotations.length; paramIndex++) {
            Annotation[] annos = paramAnnotations[paramIndex];
            List<MessageProperty> simpleMsgPropertyAnnations = parseSimpleMsgPropertyAnnations(annos);
            paramMsgProperties.add(simpleMsgPropertyAnnations);
        }
        methodConfig.setParamAnnotations(paramMsgProperties);
        // 解析方法上的MessageProperty Annotation
        Annotation[] methodAnnotations = method.getAnnotations();
        List<MessageProperty> methodMsgProps = parseSimpleMsgPropertyAnnations(methodAnnotations);
        methodConfig.setMethodAnnotations(methodMsgProps);
    }

    /**
     * 解析Method上或Parameter上的Annotation
     * 
     * @param annos
     * @return
     */
    private static List<MessageProperty> parseSimpleMsgPropertyAnnations(final Annotation[] annos) {
        if (annos != null && annos.length > 0) {
            List<MessageProperty> paramAnnoList = new ArrayList<MessageProperty>();
            for (int annoIndex = 0; annoIndex < annos.length; annoIndex++) {
                if (annos[annoIndex] instanceof MessageProperties) {
                    MessageProperties msgProps = (MessageProperties) annos[annoIndex];
                    for (MessageProperty msgProp : msgProps.value()) {
                        paramAnnoList.add(msgProp);
                    }
                } else if (annos[annoIndex] instanceof MessageProperty) {
                    paramAnnoList.add((MessageProperty) annos[annoIndex]);
                }
            }
            return paramAnnoList;
        } else {
            return Collections.emptyList();
        }
    }
}
